package app.action;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Iterator;
import java.util.Vector;

import org.apache.log4j.Logger;

public class ServerThreadHandler extends Thread {
	private final static Logger logger = Logger.getLogger(ServerThreadHandler.class);
	private StartServer startServer;
	private Socket socket;
	private int threadID = 0;
	private ObjectOutputStream oos;
	private ObjectInputStream ois;
	
	protected User me = null;

	public ServerThreadHandler(StartServer startServer, Socket socket, int threadID) throws IOException {
		this.startServer = startServer;
		this.socket = socket;
		this.threadID = threadID;
		this.setName(String.valueOf(this.threadID));

		ois = new ObjectInputStream(socket.getInputStream());
		oos = new ObjectOutputStream(socket.getOutputStream());

		this.controlOnlineUser(this, true);
		logger.info("A new client online; threadID = " + this.threadID);
	}

	@Override
	public void run() {
		Object obj = null;
		try {
			while ((obj = ois.readObject()) != null) {
				MessageHandle msgHnd = (MessageHandle) obj;

				switch (msgHnd.getType()) {
					case SYSTEM:
						doInitialize(msgHnd);
						break;
					case GENERAL:
					case PRIVATE:
						sendOutObject(msgHnd.getTargetIDs(), obj);
						break;
					default:
						break;
				}
			}
		}catch (ClassNotFoundException e) {
			logger.error(e.getMessage(), e.getCause());
		}catch (IOException e) {
			logger.info("A client with threadID = " + this.threadID + " is ofline now.");
		}finally {
			// Remove this thread from handler array
			this.controlOnlineUser(this, false);

			try {
				oos.close();
				ois.close();
				socket.close();
			}catch (IOException ioe) {
				logger.error(ioe.getMessage(), ioe.getCause());
			}
		}
	}

	/**
	 * 
	 * @param vector
	 *            Set of direction of message: =null will send to all.
	 * @param obj
	 *            message
	 * @throws IOException
	 */
	private synchronized void sendOutObject(Vector<Integer> vector, Object obj) {
		Iterator<ServerThreadHandler> iter = startServer.getServerThreadHandlers().iterator();
		ServerThreadHandler s = null;

		try {
			while (iter.hasNext()) {
				s = iter.next();

				if(vector == null || vector.contains(s.threadID)) {
					s.oos.writeObject(obj);
					s.oos.flush();
				}
			}
		}catch (IOException e) {
			logger.error(e.getMessage(), e.getCause());
		}
	}

	/**
	 * Add or remove server thread from handler vector
	 * 
	 * @param sth
	 *            the {@link ServerThreadHandler}
	 * @param b
	 *            = true for adding, and = false for remove
	 */
	protected void controlOnlineUser(ServerThreadHandler sth, boolean b) {
		if(b) {
			startServer.getServerThreadHandlers().add(sth);
		}
		else {
			startServer.getServerThreadHandlers().add(sth);
		}

		doInform();
	}

	protected void doInitialize(MessageHandle msgHnd) {
		if(me == null) {
			me = new User(threadID, msgHnd.getContent());
		}

		Vector<Object> objects = new Vector<>();
		Vector<ServerThreadHandler> sths = startServer.getServerThreadHandlers();
		for(ServerThreadHandler sth : sths) {
			objects.add(sth.me);
		}

		// Send out new connected user's ID and online users
		sendOutObject(null, new MessageHandle(startServer.me, String.valueOf(threadID), objects));
	}

	/**
	 * Tell to online client
	 */
	public void doInform() {
		sendOutObject(null, new MessageHandle(startServer.me, String.valueOf(startServer.getNumberOfOnline()), null));
	}
}
